/*
 * Copyright 2024 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.pajamas.starter.integration.module.embedded;

import com.github.benmanes.caffeine.cache.Caffeine;

import org.pajamas.starter.core.util.VmUtils;
import org.pajamas.starter.integration.spring.bean.BeanConstants;
import org.pajamas.starter.integration.spring.caching.CaffeineBuilder;
import org.pajamas.starter.integration.spring.jdbc.H2DataSourceBuilder;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.task.TaskSchedulerCustomizer;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.support.TaskUtils;

import java.util.concurrent.TimeUnit;

import javax.sql.DataSource;

/**
 * @author william
 * @since 2024/1/22
 */
@ConditionalOnProperty(prefix = "org.pajamas.starter.embedded", value = "enabled", havingValue = "true",
        matchIfMissing = true)
@EnableCaching(proxyTargetClass = true)
@EnableScheduling
public class PajamasEmbeddedAutoConfiguration {
    @ConditionalOnEmbeddedDdBuilding
    @Bean
    public H2DataSourceBuilder pajamasH2DataSourceBuilder(@Value("${spring.application.name}") String appName) {
        return H2DataSourceBuilder
                .newBuilder()
                .embeddedMode()
                .userHome(appName)
                .compatibilityMode("MYSQL")
                .username("root")
                .password("root");
    }

    @ConditionalOnBean(H2DataSourceBuilder.class)
    @Primary
    @Bean
    public DataSource pajamasEmbeddedDataSource(H2DataSourceBuilder builder,
                                                ObjectProvider<H2DataSourceBuilder.Customizer> customizers) {
        return builder.customizers(customizers.orderedStream().iterator()).build();
    }

    @ConditionalOnProperty(prefix = "org.pajamas.starter.embedded", value = "cache", havingValue = "true",
            matchIfMissing = true)
    @ConditionalOnMissingBean(CaffeineBuilder.class)
    @Bean
    public CaffeineBuilder pajamasCaffeineBuilder() {
        return CaffeineBuilder.newBuilder()
                .size(256)
                .expireAfterWrite(5, TimeUnit.MINUTES)
                .expireAfterAccess(25, TimeUnit.SECONDS);
    }

    @ConditionalOnBean(CaffeineBuilder.class)
    @Primary
    @Bean
    public CacheManager pajamasEmbeddedCacheManager(CaffeineBuilder builder,
                                                    ObjectProvider<CaffeineBuilder.Customizer> customizers) {
        Caffeine<Object, Object> caffeine = builder.customizers(customizers.orderedStream().iterator()).build();
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        cacheManager.setCaffeine(caffeine);
        return cacheManager;
    }

    @ConditionalOnProperty(prefix = "org.pajamas.starter.embedded", value = "scheduler", havingValue =
            "true", matchIfMissing = true)
    @Order(BeanConstants.ORDER_LATEST_SECOND)
    @Bean
    public TaskSchedulerCustomizer pajamasTaskSchedulerCustomizer() {
        return taskScheduler -> {
            int corsNumber = VmUtils.getRuntimeCorsNumber();
            int defaultPoolSize = corsNumber + 1;
            taskScheduler.setThreadNamePrefix("PAJAMAS-SCHEDULER-TASK-");
            taskScheduler.setPoolSize(defaultPoolSize);
            taskScheduler.setWaitForTasksToCompleteOnShutdown(false);
            taskScheduler.setErrorHandler(TaskUtils.LOG_AND_PROPAGATE_ERROR_HANDLER);
        };
    }
}
